package cn.lg.soar.system.biz.filetemplate;

import cn.lg.soar.common.util.AssertUtil;
import cn.lg.soar.common.util.FileTypeUtils;
import cn.lg.soar.system.biz.filetemplate.model.GeneratePresignedUrlRequestDTO;
import cn.lg.soar.system.biz.filetemplate.model.PartUploadEndDTO;
import cn.lg.soar.system.biz.filetemplate.model.PartUploadStartDTO;
import cn.lg.soar.system.biz.modules.general.entity.FileStorage;
import cn.lg.soar.system.biz.util.FileAccessTokenUtils;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.Protocol;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.*;
import lombok.SneakyThrows;
import org.springframework.util.Assert;

import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * @author luguoxiang 469599794@qq.com
 * @Date: 2024/3/1 17:25
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class AmazonS3FileTemplateImpl implements FileTemplate {

    private final AmazonS3 amazonS3;
    private final int configVersion;
    private final int storageId;
    private final String bucketName;
    private final BiFunction<String, String, String> urlGenerator;

    public AmazonS3FileTemplateImpl(FileStorage storage) {
        this.configVersion = storage.getVersion();
        this.storageId = storage.getId();
        this.bucketName = storage.getBucketName();
        String endpoint = storage.getEndpoint();
        String accessKey = storage.getAccessKey();
        String secretKey = storage.getSecretKey();
        // url以及region配置
        AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder.EndpointConfiguration(
                endpoint,
                null
        );
        ClientConfiguration clientConfig = new ClientConfiguration();
        clientConfig.setProtocol(Protocol.HTTPS);

        AWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
        AWSCredentialsProvider awsCredentialsProvider = new AWSStaticCredentialsProvider(awsCredentials);

        // 创建S3客户端
        amazonS3 = AmazonS3Client.builder()
                .withEndpointConfiguration(endpointConfiguration)
                .withClientConfiguration(clientConfig)
                .withCredentials(awsCredentialsProvider)
                .disableChunkedEncoding()
                .withPathStyleAccessEnabled(storage.getPathStyleAccess())
                .withForceGlobalBucketAccessEnabled(true)
                .build();
        switch (storage.getAccessMode()) {
            case direct:
                this.urlGenerator = (directory, filename) -> amazonS3.getUrl(bucketName, getObjectName(directory, filename)).toString();
                break;
            case redirect:
                this.urlGenerator = (directory, filename) -> FileAccessTokenUtils.createRedirectUrl(storageId, directory, filename);
                break;
            default:
                this.urlGenerator = (directory, filename) -> FileAccessTokenUtils.createProxyUrl(storageId, directory, filename);
                break;
        }
    }

    @Override
    public void upload(String directory, String filename, InputStream stream) {
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(FileTypeUtils.getContentType(filename));
        amazonS3.putObject(bucketName, getObjectName(directory, filename), stream, objectMetadata);
    }

    @Override
    @SneakyThrows
    public String getAccessUrl(String directory, String filename, long expire) {
        expire = System.currentTimeMillis() + expire;
        return amazonS3.generatePresignedUrl(bucketName, getObjectName(directory, filename), new Date(expire))
                .toString();
    }

    @Override
    public String getAccessUrl(String directory, String filename) {
        return this.urlGenerator.apply(directory, filename);
    }

    @Override
    @SneakyThrows
    public void removeFile(String directory, String filename) {
        amazonS3.deleteObject(bucketName, getObjectName(directory, filename));
    }

    @Override
    public InputStream getInputStream(String directory, String filename) {
        return amazonS3.getObject(bucketName, getObjectName(directory, filename)).getObjectContent();
    }

    @Override
    @SneakyThrows
    public S3Object getFile(String directory, String filename) {
        return amazonS3.getObject(bucketName, getObjectName(directory, filename));
    }

    @Override
    @SneakyThrows
    public List<S3ObjectSummary> getFileList(String prefix) {
        ObjectListing objectListing = amazonS3.listObjects(bucketName, prefix);
        return objectListing.getObjectSummaries();
    }

    @Override
    public boolean isExist(String directory, String filename) {
        return amazonS3.doesObjectExist(bucketName, getObjectName(directory, filename));
    }

    @Override
    public int getConfigVersion() {
        return configVersion;
    }

    @Override
    public void test() {
        amazonS3.listBuckets();
        AssertUtil.isTrue(amazonS3.doesBucketExistV2(bucketName), "目标桶不存在");
    }

    @Override
    public void disconnect() {
        // TODO 关闭连接
    }

    @Override
    public URL generatePresignedUrl(GeneratePresignedUrlRequestDTO dto) {
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(
                bucketName,
                getObjectName(dto.getDirectory(), dto.getFilename()),
                dto.getMethod()
        )
                .withExpiration(dto.getExpireDate());
        Map<String, String> requestParameters = dto.getRequestParameters();
        if (requestParameters != null) {
            requestParameters.forEach(request::addRequestParameter);
        }
        // 获取上传分片的地址
        return amazonS3.generatePresignedUrl(request);
    }

    @Override
    public String startMultipartUpload(PartUploadStartDTO dto) {
        String directory = dto.getDirectory();
        String filename = dto.getFilename();
        // 构建上传任务参数
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentType(dto.getContentType());

        // 生成上传任务id
        InitiateMultipartUploadResult initiateMultipartUploadResult = amazonS3
                .initiateMultipartUpload(new InitiateMultipartUploadRequest(bucketName, getObjectName(directory, filename))
                        .withObjectMetadata(objectMetadata));
        return initiateMultipartUploadResult.getUploadId();
    }

    @Override
    public void endMultipartUpload(PartUploadEndDTO dto) {
        String uploadId = dto.getUploadId();
        int partCount = dto.getPartCount();
        String directory = dto.getDirectory();
        String filename = dto.getFilename();
        ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, getObjectName(directory, filename), uploadId);
        List<PartSummary> parts = amazonS3.listParts(listPartsRequest).getParts();
        Assert.isTrue(parts.size() == partCount, "分片数量不匹配");
        List<PartETag> partETags = parts.stream()
                .map(x -> new PartETag(x.getPartNumber(), x.getETag()))
                .collect(Collectors.toList());
        CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest()
                .withUploadId(uploadId)
                .withKey(filename)
                .withBucketName(directory)
                .withPartETags(partETags);
        CompleteMultipartUploadResult result = amazonS3.completeMultipartUpload(completeMultipartUploadRequest);
//        return result.getLocation();
    }

    private String getObjectName(String directory, String filename) {
        return directory + "/" + filename;
    }
}